#include "ui_message_server.h"
#include "ui_application.h"
#include "ui_std_ex.h"
#include "ui_message_client.h"
#include "ui_error.h"
#include <sstream>
#include <algorithm>
#include <functional>
#include <SDL/SDL.h>


namespace UI
{

UI_MessageServer* UI_MessageServer::m_pInstance = 0;


UI_MessageServer::UI_MessageServer(void) : m_bIgnoreAllNewMessages(true)
{
	m_pSemaphore = SDL_CreateSemaphore(0);
}


UI_MessageServer::~UI_MessageServer(void)
{

}


UI_MessageServer& UI_MessageServer::Instance(void)
{
	if (!m_pInstance)
	{
		m_pInstance = new UI_MessageServer;
		if (!m_pInstance)
		{
			throw(UI_Ex_App("CMessageServer::Instance : Unable to instantiate Message Server!"));
		}
	}

	return *m_pInstance;
}


void UI_MessageServer::RegisterMessageClient(UI_MessageClient* pClient, UI_Message::EMessageType eMessageType, unsigned char Priority)
{
	if (!pClient)
	{
		UI_Application::Instance()->GetApplicationLog().AddLogEntry(
			"CMessageServer::RegisterMessageClient : Attempting to register a non-existent message client.", APP_LOG_ERROR);
	}
	else
	{
		m_MessageClients[eMessageType].insert(std::make_pair(Priority, s_MessageClientActive(pClient, false)));
	}
}


void UI_MessageServer::DeregisterMessageClient(UI_MessageClient* pClient, UI_Message::EMessageType eMessageType)
{
	t_MessageClientPriorityMap& PriorityMap = m_MessageClients[eMessageType];
	t_MessageClientPriorityMap::iterator iter = PriorityMap.begin();
	while (iter != PriorityMap.end())
	{
		if (iter->second.pClient == pClient)
		{
			PriorityMap.erase(iter);
			iter = PriorityMap.begin();
		}
		else
		{
			++iter;
		}
	}
}


void UI_MessageServer::DeregisterMessageClient(UI_MessageClient* pClient)
{
	for (t_MessageClientMap::iterator iter = m_MessageClients.begin(); iter != m_MessageClients.end(); ++iter)
	{
		t_MessageClientPriorityMap::iterator iter2 = iter->second.begin();
		while (iter2 != iter->second.end())
		{
			if (iter2->second.pClient == pClient)
			{
				iter->second.erase(iter2);
				iter2 = iter->second.begin();
			}
			else
			{
				++iter2;
			}
		}
	}
}


void UI_MessageServer::DeliverMessage(void)
{
	if (m_MessageQueue.size() > 0)
	{
		UI_Message* pMessage = m_MessageQueue.front();
		t_MessageClientPriorityMap& PriorityMap = m_MessageClients[pMessage->MessageType()];

		// we have to make sure that each client only gets the message once,
		// even if the handling of one of these messages changes the message map
		for (t_MessageClientPriorityMap::iterator iter = PriorityMap.begin(); iter != PriorityMap.end(); ++iter)
		{
			iter->second.bWaitingForMessage = true;
		}

		bool bFinished = false;
		while (! bFinished)
		{
			t_MessageClientPriorityMap::iterator iter = PriorityMap.begin();
			for (; iter != PriorityMap.end(); ++iter)
			{
				if (iter->second.bWaitingForMessage)
				{
					iter->second.bWaitingForMessage = false;
					if(iter->second.pClient->IfReceiveEvent())
						bFinished = iter->second.pClient->HandleMessage(pMessage);
					break;
				}
			}
			if (iter == PriorityMap.end())
			{
				bFinished = true;
			}
		}

		m_MessageQueue.pop_front();
		delete pMessage;
	}
}


//! A functor for finding duplicate APP_PAINT messages

struct Duplicate_APP_PAINT : public std::unary_function<UI_Message*, bool>
{
public:
	//! The functor constructor
	//! \param pClient The destination of the message that is being checked
	Duplicate_APP_PAINT(const UI_MessageClient* pClient) : m_pClient(pClient) { }
	//! Checks to see if the message is a duplicate of an existing APP_PAINT message
	//! \param pMessage A pointer to the message that is being checked against
	//! \return true of the message is a duplicate
	bool operator() (UI_Message* pMessage) const
	{
		bool bResult = (pMessage->MessageType() == UI_Message::APP_PAINT) && (pMessage->Destination() == m_pClient);
		return bResult;
	}

private:
	//! The destination of the message that is being checked
	const UI_MessageClient* m_pClient;
};


void UI_MessageServer::QueueMessage(UI_Message* pMessage)
{
	if (!m_bIgnoreAllNewMessages)
	{
		// check for and remove any redundant APP_PAINT messages in the queue
		if (pMessage->MessageType() == UI_Message::APP_PAINT)
		{
			m_MessageQueue.erase(std::remove_if(m_MessageQueue.begin(), m_MessageQueue.end(), Duplicate_APP_PAINT(pMessage->Destination())), m_MessageQueue.end());
		}

		m_MessageQueue.push_back(pMessage);
	}
}

}
